#include "zglEngine.h"
#include "zglTexture.h"
#include "zglFont.h"
#include "zglDrawContext.h"
#include "zglRenderTarget.h"
#include "sStrings.h"

namespace sgui
{
namespace engine
{
	const int zglEngine::KEY_MAPPINGS_DEF[obs::sKeyboardEvent::KEY_COUNT] = {
		K_SYSRQ, K_PAUSE, K_ESCAPE, K_ENTER, K_KP_ENTER, K_UP, K_DOWN, K_LEFT, K_RIGHT, K_BACKSPACE, K_SPACE, K_TAB, K_TILDE, K_INSERT, K_DELETE,
		K_HOME, K_END, K_PAGEUP, K_PAGEDOWN, K_CTRL, K_CTRL_L, K_CTRL_R, K_ALT, K_ALT_L, K_ALT_R, K_SHIFT, K_SHIFT_L, K_SHIFT_R, K_SUPER, K_SUPER_L,
		K_SUPER_R, K_APP_MENU, K_CAPSLOCK, K_NUMLOCK, K_SCROLL, K_BRACKET_L, K_BRACKET_R, K_BACKSLASH, K_SLASH, K_COMMA, K_DECIMAL, K_SEMICOLON,
		K_APOSTROPHE, K_0, K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9, K_MINUS, K_EQUALS, K_A, K_B, K_C, K_D, K_E, K_F, K_G, K_H, K_I, K_J, K_K, K_L, 
		K_M, K_N, K_O, K_P, K_Q, K_R, K_S, K_T, K_U, K_V, K_W, K_X, K_Y, K_Z, K_KP_0, K_KP_1, K_KP_2, K_KP_3, K_KP_4, K_KP_5, K_KP_6, K_KP_7, K_KP_8, 
		K_KP_9, K_KP_SUB, K_KP_ADD, K_KP_MUL, K_KP_DIV, K_KP_DECIMAL, K_F1, K_F2, K_F3, K_F4, K_F5, K_F6, K_F7, K_F8, K_F9, K_F10, K_F11, K_F12,
	};

    sTexture* zglEngine::getTexture(const res::sTextureResource& res, const preset::sTexturePreset& preset)
    {
        return new zglTexture(*this, res, preset);
    }
    
    sFont* zglEngine::getFont(const res::sFontResource& res, const preset::sFontPreset& preset)
    {
        return new zglFont(*this, res, preset);
    }
	
	sRenderTarget* zglEngine::getRenderTarget(const math::sPointf& size)
	{
		return new zglRenderTarget(*this, size);
	}
	
	void zglEngine::getContext(void *memory, const math::sRectf& rect, bool clipRect) const
	{
		new (memory) zglDrawContext(*this, rect, clipRect);
	}

	void zglEngine::getContext(void *memory, const math::sRectf& rect, const sDrawContext* parentContext, bool clipRect) const
	{
		new (memory) zglDrawContext(*this, rect, parentContext, clipRect);
	}
	
	u32 zglEngine::getContextMemorySize() const
	{
		return sizeof(zglDrawContext);
	}
		
	math::sPointf zglEngine::retreiveScreenSize() const
	{
		return math::sPointf((f32)zgl_Get(WINDOW_WIDTH), (f32)zgl_Get(WINDOW_HEIGHT));
	}

	void zglEngine::setScissor(const math::sRectf& rect, sDrawSurface& surface) const
	{
		const math::sPointf screnSize = surface.getSurfaceSize();

		glScissor(
			std::max<int>(
			  0,  
			  (int)rect.X
			),
			std::max<int>(
			  0,
			  (int)(screnSize.Y - rect.Y - rect.H)
			), rect.W, rect.H);

		glEnable(GL_SCISSOR_TEST);	
	}

	void zglEngine::removeScissor() const
	{
		glDisable(GL_SCISSOR_TEST);
	}
	
	void zglEngine::beginRender(sDrawStack& stack) const
	{
		glLoadIdentity();
	}

	void zglEngine::endRender(sDrawStack& stack) const
	{
		//
	}

	void zglEngine::textBeginRead()
	{
		key_BeginReadText("");
		m_readTextMode = true;
	}

	void zglEngine::textEndRead()
	{
		key_EndReadText();
		m_readTextMode = false;
	}

	void zglEngine::update(double dt)
	{
		math::sPointf m_mousePos((float)mouse_X(), (float)mouse_Y());
		if (m_lastMousePos != m_mousePos)
		{
			notify(new obs::sMouseMoveEvent(m_mousePos, m_lastMousePos));
		}
		m_lastMousePos = m_mousePos;
		
		for (int i = 0; i < obs::sMouseEvent::EMB_COUNT; ++i)
		{
			if (mouse_Click(i))
			{
				notify(new obs::sMousePressEvent(m_mousePos, (obs::sMouseEvent::EMouseButton)i));
				mouse_ClearState();
			}
			if (mouse_Up(i))
			{
				notify(new obs::sMouseReleaseEvent(m_mousePos, (obs::sMouseEvent::EMouseButton)i));
				mouse_ClearState();
			}
		}

		int keyPressId = key_Last(KA_DOWN);
		if (keyPressId != 0)
		{
			std::map<int, int>::iterator pos = m_keyMappings.find(keyPressId);
			if (pos != m_keyMappings.end())
			{
				int keyCodeId = pos->second;
				
				notify(new obs::sKeyboardPressEvent((obs::sKeyboardPressEvent::EKeyButton)keyCodeId));
			}
			key_ClearState();
		}

		int keyReleaseId = key_Last(KA_UP);
		if (keyReleaseId != 0)
		{
			std::map<int, int>::iterator pos = m_keyMappings.find(keyReleaseId);
			if (pos != m_keyMappings.end())
			{
				int keyCodeId = pos->second;
				
				notify(new obs::sKeyboardReleaseEvent((obs::sKeyboardPressEvent::EKeyButton)keyCodeId));
			}
			key_ClearState();
		}

		if (m_readTextMode)
		{
			std::string textRead_c(key_GetText());
			if (!textRead_c.empty())
			{
				std::wstring textRead(string_to_wstring(textRead_c));
				for (std::wstring::iterator it = textRead.begin(); it != textRead.end(); it++)
				{
					notify(new obs::sKeyboardCharEvent(*it));
				}
				key_BeginReadText("");
			}
		}
	}

	void zglEngine::drawRect(const math::sRectf& rect, u32 color, bool fill) const
    {
		if (fill)
		{
			pr2d_Rect(rect.X, rect.Y, rect.W, rect.H, color & 0xFFFFFF, color >> 24, PR2D_FILL);
		}
		else
		{
			pr2d_Rect(rect.X, rect.Y, rect.W, rect.H, color & 0xFFFFFF, color >> 24);
		}
    }

    zglEngine::zglEngine() :
		m_readTextMode(false)
    {
		zglLoad(libZenGL);
		zgl_Disable(CLIP_INVISIBLE);

		for (int i = 0; i < obs::sKeyboardEvent::KEY_COUNT; ++i)
		{
			m_keyMappings.insert(std::pair<int, int>(KEY_MAPPINGS_DEF[i], i));
		}
    }

};
};